package lucava.metamodel.java.visitor;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import lucava.metamodel.java.Annotations;
import lucava.metamodel.java.Attribute;
import lucava.metamodel.java.Comments;
import lucava.metamodel.java.MetaClasse;
import lucava.metamodel.java.MetaClassifier;
import lucava.metamodel.java.MetaInterface;
import lucava.metamodel.java.MetaModel;
import lucava.metamodel.java.MetaPackage;
import lucava.metamodel.java.Methode;
import lucava.metamodel.java.MethodeBody;
import lucava.metamodel.java.MethodeSignature;
import lucava.metamodel.java.Modifier;
import lucava.metamodel.java.Parameter;
import lucava.metamodel.java.Variable;
import lucava.metamodel.java.Visibility;
import lucava.metamodel.java.type.MetaParametricType;
import lucava.metamodel.java.type.MetaType;
import lucava.util.FileBuffer;

/**
 * Générateur des fichiers .java correspondants au MetaModel visité.
 */
public class JavaFileGenerator implements IJavaMetaModelVisitor {

	/**
	 * Répertoire racine des fichiers à générer.
	 */
	protected String mRootFolder = "generated";

	/**
	 * Fichier en cours de génération.
	 */
	protected FileBuffer mCurrentFileBuffer;

	/**
	 * MetaModel en cours de génération
	 */
	protected MetaModel mCurrentMetaModel;

	/**
	 * Booléen indiquant si le visiteur doit être généré.
	 */
	protected boolean mShouldGenerateVisitor = true;

	/**
	 * Liste des fichiers générés.
	 */
	protected final List<FileBuffer> mGeneratedFiles = new ArrayList<FileBuffer>();

	/**
	 * Constructeur.
	 * 
	 * @param aFolderPath
	 *            , chemin du répertoire dans lequel généré les fichier.
	 */
	public JavaFileGenerator(String aFolderPath) {
		String folderPath = "generated";

		if (aFolderPath != null && !aFolderPath.isEmpty()) {
			folderPath = aFolderPath + System.getProperty("file.separator")
					+ folderPath;
		}
		this.mRootFolder = folderPath;
	}

	/**
	 * Constructeur.
	 */
	public JavaFileGenerator() {
		this(null);
	}

	/**
	 * Ouvre le flux d'écriture sur le fichier pour le classifier. <br/>
	 * Le fichier est placé dans l'arborescence du Package.
	 * 
	 * @param aClassifier
	 *            , Classifier pour lequel ouvrir le flux d'écriture.
	 */
	protected void createFileForClassifier(MetaClassifier aClassifier) {
		MetaPackage classifierPackage = aClassifier.getPackage();

		// Récupération du nom complet du package
		String[] folders = classifierPackage.getCompleteName().split("\\.");
		String currentPath = mRootFolder;

		// Création du répertoire racine si besoin
		this.createFolderIfNonExistent(mRootFolder);

		// Ajout du séparateur de fichier.
		currentPath += System.getProperty("file.separator");

		// Parcours de l'arborescence du package
		for (int i = 0; i < folders.length; i++) {
			currentPath += folders[i];

			// Création du répertoire si besoin
			this.createFolderIfNonExistent(currentPath);

			if (!folders[i].isEmpty()) {
				// Ajout du séparateur de fichier.
				currentPath += System.getProperty("file.separator");
			}
		}

		// Ouverture du flux d'écriture sur le fichier
		String fileName = currentPath + aClassifier.getName() + ".java";
		this.mCurrentFileBuffer = new FileBuffer(fileName);
		this.mGeneratedFiles.add(mCurrentFileBuffer);
	}

	/**
	 * Créer le répertoire correspondant au chemin si celui n'existe pas.
	 * 
	 * @param aPath
	 *            , chemin du répertoire.
	 * @throws RuntimeException
	 *             Si le chemin indiqué pointe sur un fichier non répertoire
	 */
	protected void createFolderIfNonExistent(String aPath) {
		File testFile = new File(aPath);

		// Si aucun fichier n'est présent au chemin indiqué
		if (!testFile.exists()) {

			System.out
					.println("Le répertoire spécifié n'existe pas. Création du répertoire '"
							+ aPath + "'");
			// Création du répertoire correspondant.
			testFile.mkdir();
		}
		// Si un fichier existe pour le chemin
		else {

			// Si le fichier n'est pas un répertoire
			if (!testFile.isDirectory()) {
				throw new RuntimeException(
						"Un fichier est déjà présent pour le chemin du répertoire '"
								+ aPath + "'");
			}
		}
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitMetaModel(lucava.metamodel.java.MetaModel)
	 */
	@Override
	public void visitMetaModel(MetaModel aMetaModel) {

		this.mCurrentMetaModel = aMetaModel;

		// Génération de toutes les classes et interfaces du MetaModel
		Iterator<MetaClassifier> classifierIt = aMetaModel.getClassifier()
				.iterator();
		while (classifierIt.hasNext()) {
			classifierIt.next().accept(this);
		}

		// Si le MetaModel est visitable
		if (aMetaModel.isVisitable()) {
			// Si le visiteur doit être généré.
			if (this.shouldGenerateVisitor()) {
				this.generateVisitor();
			}
		}
	}

	/**
	 * Génère l'interface du visteur du MetaModel généré.
	 */
	protected void generateVisitor() {

		// Création du fichier de l'interface
		this.createFileForClassifier(this.mCurrentMetaModel.getVisitor());

		// Remplissage du fichier de l'interface du visiteur
		this.printPackage(this.mCurrentMetaModel.getVisitor().getPackage());

		this.mCurrentFileBuffer.printLn("public interface "
				+ this.mCurrentMetaModel.getVisitor().getName() + "{");
		this.mCurrentFileBuffer.increaseTab();

		// Génération des méthodes du visiteur
		Iterator<MetaClassifier> classifierIt = this.mCurrentMetaModel
				.getClassifier().iterator();
		while (classifierIt.hasNext()) {
			this.generateVisitMethod(classifierIt.next());
		}

		// Fermeture du fichier de l'interface du visiteur
		this.mCurrentFileBuffer.decreaseTab();
		this.mCurrentFileBuffer.printLnWithTab("}");

		this.mCurrentFileBuffer = null;

	}

	/**
	 * Génère la méthode de visite d'un Classifier.
	 * 
	 * @param aClassifier
	 *            , Classifier pour lequel générer la méthode de visite.
	 */
	protected void generateVisitMethod(MetaClassifier aClassifier) {

		this.mCurrentFileBuffer.printNewLine();

		// Ecriture de la signature de la methode de visite
		this.mCurrentFileBuffer.printLnWithTab("public void visit"
				+ aClassifier.getName() + "(" + aClassifier.getCompleteName()
				+ " "
				+ lucava.util.StringUtils.addArticleTo(aClassifier.getName())
				+ ");");
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitInterface(lucava.metamodel.java.MetaInterface)
	 */
	@Override
	public void visitInterface(MetaInterface aMetaInterface) {

		this.createFileForClassifier(aMetaInterface);

		this.printPackage(aMetaInterface.getPackage());

		if (!aMetaInterface.getVisibility().isProtected()) {
			aMetaInterface.getVisibility().accept(this);
		}

		aMetaInterface.getModifier().accept(this);

		this.mCurrentFileBuffer.print("interface " + aMetaInterface.getName()
				+ " ");

		// Si des héritages sont présent, écriture des liens d'héritage
		if (!aMetaInterface.getExtends().isEmpty()) {

			this.mCurrentFileBuffer.print("extends ");

			// Ajout de tous les héritages
			Iterator<MetaInterface> heritIt = aMetaInterface.getExtends()
					.iterator();
			while (heritIt.hasNext()) {

				// Ecriture de l'héritage courant.
				this.mCurrentFileBuffer.print(heritIt.next().getCompleteName());

				// S'il reste des héritages
				if (heritIt.hasNext()) {
					// Ajout d'un séparateur
					this.mCurrentFileBuffer.print(", ");
				}
			}
		}

		this.mCurrentFileBuffer.printLn("{");
		this.mCurrentFileBuffer.increaseTab();

		this.mCurrentFileBuffer.printNewLine();

		// Génération de toutes les signatures de l'interface
		Iterator<MethodeSignature> signatureIt = aMetaInterface.getMethods()
				.iterator();
		while (signatureIt.hasNext()) {
			signatureIt.next().accept(this);
			this.mCurrentFileBuffer.printLn(";");
		}

		this.mCurrentFileBuffer.decreaseTab();
		this.mCurrentFileBuffer.printLnWithTab("}");

		this.mCurrentFileBuffer = null;
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitClasse(lucava.metamodel.java.MetaClasse)
	 */
	@Override
	public void visitClasse(MetaClasse aMetaClasse) {

		this.createFileForClassifier(aMetaClasse);

		this.printPackage(aMetaClasse.getPackage());

		// Ajout des commentaires
		this.generateComment(aMetaClasse);

		// Ajout des annotations
		aMetaClasse.getAnnotations().accept(this);

		if (!aMetaClasse.getVisibility().isProtected()) {
			aMetaClasse.getVisibility().accept(this);
		}

		aMetaClasse.getModifier().accept(this);

		this.mCurrentFileBuffer.print("class " + aMetaClasse.getName() + " ");

		// Si un héritage est présent, écriture de lien d'héritage
		if (aMetaClasse.getExtends() != null) {

			this.mCurrentFileBuffer.print("extends ");

			this.mCurrentFileBuffer.print(aMetaClasse.getExtends()
					.getCompleteName());

		}

		// Si des héritages sont présent, écriture des liens d'héritage
		if (!aMetaClasse.getImplements().isEmpty()) {

			this.mCurrentFileBuffer.print("implements ");

			// Ajout de tous les intérfaces
			Iterator<MetaInterface> heritIt = aMetaClasse.getImplements()
					.iterator();
			while (heritIt.hasNext()) {

				// Ecriture de l'héritage courant.
				this.mCurrentFileBuffer.print(heritIt.next().getCompleteName());

				// S'il reste des héritages
				if (heritIt.hasNext()) {
					// Ajout d'un séparateur
					this.mCurrentFileBuffer.print(", ");
				}
			}
		}

		this.mCurrentFileBuffer.printLn("{");

		this.mCurrentFileBuffer.printNewLine();

		this.mCurrentFileBuffer.increaseTab();

		// Génération de tous les attributs de la classe
		Iterator<Attribute> attributeIt = aMetaClasse.getAttributes()
				.iterator();
		while (attributeIt.hasNext()) {
			attributeIt.next().accept(this);
		}

		// Génération de toutes les méthodes de la classe
		Iterator<Methode> methodeIt = aMetaClasse.getMethods().iterator();
		while (methodeIt.hasNext()) {
			methodeIt.next().accept(this);
		}

		// / Génération des getter et des setters des attributs
		attributeIt = aMetaClasse.getAttributes().iterator();
		Attribute currentAttribute;
		while (attributeIt.hasNext()) {
			currentAttribute = attributeIt.next();

			// Génération du Getter.
			this.generateGetterForAttribute(currentAttribute);
			this.mCurrentFileBuffer.printNewLine();

			// Génération du Setter (s'il l'attribut n'est pas final)
			if (!currentAttribute.getModifier().isFinal()) {
				this.generateSetterForAttribute(currentAttribute);
				this.mCurrentFileBuffer.printNewLine();
			}
		}

		// Si le MetaModel est visitable
		if (this.mCurrentMetaModel.isVisitable()) {
			// Génération de la méthode accept.
			this.generateAcceptMethod(aMetaClasse);
		}

		this.mCurrentFileBuffer.decreaseTab();
		this.mCurrentFileBuffer.printLnWithTab("}");

		this.mCurrentFileBuffer = null;
	}

	/**
	 * Génère la méthode d'acceptation du visiteur de MetaModel Java.
	 * 
	 * @param aMetaClasse
	 *            , Classe pour laquelle la méthode est à générer.
	 */
	protected void generateAcceptMethod(MetaClasse aMetaClasse) {

		// Commentaire de la méthode
		this.mCurrentFileBuffer.printLnWithTab("/**");
		this.mCurrentFileBuffer
				.printLnWithTab("* Méthode d'acceptation du visiteur.");
		this.mCurrentFileBuffer.printLnWithTab("*/");

		// Méthode accept
		this.mCurrentFileBuffer.printLnWithTab("public void accept("
				+ this.mCurrentMetaModel.getVisitor().getCompleteName()
				+ " aVisitor){");
		this.mCurrentFileBuffer.increaseTab();
		this.mCurrentFileBuffer.printLnWithTab("aVisitor.visit"
				+ aMetaClasse.getName() + "(this);");
		this.mCurrentFileBuffer.decreaseTab();
		this.mCurrentFileBuffer.printLnWithTab("}");
	}

	/**
	 * Écrit la description du package.
	 * 
	 * @param aPackage
	 *            package dont le nom est à écrire en début de fichier.
	 */
	protected void printPackage(MetaPackage aPackage) {

		if (!aPackage.isEmpty()) {
			this.mCurrentFileBuffer.print("package ");
			aPackage.accept(this);
			this.mCurrentFileBuffer.printLn(";");
			this.mCurrentFileBuffer.printNewLine();
		}
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitPackage(lucava.metamodel.java.MetaPackage)
	 */
	@Override
	public void visitPackage(MetaPackage aPackage) {
		// Ecriture du nom du package.
		this.mCurrentFileBuffer.print(aPackage.getCompleteName());
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitVisibility(lucava.metamodel.java.Visibility)
	 */
	@Override
	public void visitVisibility(Visibility aVisibility) {
		this.mCurrentFileBuffer.print(aVisibility.getVisibility() + " ");
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitType(lucava.metamodel.java.type.MetaType)
	 */
	@Override
	public void visitType(MetaType aMetaType) {

		// Ecriture du nom du type
		this.mCurrentFileBuffer.print(aMetaType.getCompleteName());
	}

	/**
	 * Génère le commentaire d'une méthode.
	 * 
	 * @param aMethode
	 *            , Méthode dont le commentaire est à générer.
	 */
	protected void generateMethodComment(Methode aMethode) {

		// Si des commentaires sont présent
		if (!aMethode.hasComment()) {

			// Début du commentaire
			this.mCurrentFileBuffer.printLnWithTab("/**");

			aMethode.getComments().accept(this);

			// Génération de toutes les lignes de commentaires des paramètres
			Iterator<Parameter> paramIt = aMethode.getParams().iterator();
			Parameter currentParam;
			while (paramIt.hasNext()) {
				currentParam = paramIt.next();
				this.mCurrentFileBuffer.printWithTab("* @param");
				this.mCurrentFileBuffer.printLn(currentParam.getName());

				// Décalage de l'indentation
				this.mCurrentFileBuffer.increaseTab();

				// Ajout de toutes les lignes de commentaire du paramètre
				aMethode.getComments().accept(this);

				// Décalage de l'indentation
				this.mCurrentFileBuffer.decreaseTab();
			}

			// Fin du commentaire
			this.mCurrentFileBuffer.printLnWithTab("*/");
		}

	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitMethode(lucava.metamodel.java.Methode)
	 */
	@Override
	public void visitMethode(Methode aMetaMethode) {

		// Ecriture de la signature de la méthode
		aMetaMethode.getSignature().accept(this);

		// Si la méthode n'est pas abstraite
		if (!aMetaMethode.isAbstract()) {

			// Si la methode n'a pas de corps et qu'elle a un retour
			if (aMetaMethode.getBody().isEmpty()
					&& !aMetaMethode.getReturnType().isVoid()) {

				// Ajout d'un corps ficitf pour éviter des problèmes de
				// compilation
				aMetaMethode.getBody()
						.addContent("// TODO Autogenerated body!");
				aMetaMethode.getBody().addContent("return null;");
			}

			// Ecriture du corps de la méthode
			aMetaMethode.getBody().accept(this);
		} else {

			// sinon, fin de ligne
			this.mCurrentFileBuffer.printLn(";");
		}

		this.mCurrentFileBuffer.printNewLine();
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitMethodeBody(lucava.metamodel.java.MethodeBody)
	 */
	@Override
	public void visitMethodeBody(MethodeBody aMethodeBody) {

		this.mCurrentFileBuffer.printLn("{");

		this.mCurrentFileBuffer.increaseTab();

		// Ecriture de toutes les ligne du corps de la méthode
		String[] lineTable = aMethodeBody.getContents().split(
				System.getProperty("line.separator"));

		for (int i = 0; i < lineTable.length; i++) {
			// Ajout du décalage avant chacune des lignes de la méthode.
			this.mCurrentFileBuffer.printLnWithTab(lineTable[i]);
		}

		this.mCurrentFileBuffer.decreaseTab();

		this.mCurrentFileBuffer.printLnWithTab("}");
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitSignature(lucava.metamodel.java.MethodeSignature)
	 */
	@Override
	public void visitSignature(MethodeSignature aMetaSignature) {

		// Ajout des commentaires
		this.generateComment(aMetaSignature);

		// Ajout des annotations
		aMetaSignature.getAnnotations().accept(this);

		this.mCurrentFileBuffer.printWithTab("");

		// Ajout de la visibilité
		aMetaSignature.getVisibility().accept(this);

		// Ajout des modifiers.
		aMetaSignature.getModifier().accept(this);

		// Ajout du type de retour
		aMetaSignature.getReturnType().accept(this);

		// Ajout du nom de la signature
		this.mCurrentFileBuffer.print(" " + aMetaSignature.getName() + "(");

		// Ajout de tous les paramètres
		Iterator<Parameter> paramIt = aMetaSignature.getParams().iterator();
		while (paramIt.hasNext()) {

			// Ecriture du paramètre courant.
			paramIt.next().accept(this);

			// S'il reste des paramètres
			if (paramIt.hasNext()) {
				// Ajout d'un séparateur
				this.mCurrentFileBuffer.print(", ");
			}
		}

		this.mCurrentFileBuffer.print(")");
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitVariable(lucava.metamodel.java.Variable)
	 */
	@Override
	public void visitVariable(Variable aVariable) {
		// Do nothing...
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitParametricType(lucava.metamodel.java.type.MetaParametricType)
	 */
	@Override
	public void visitParametricType(MetaParametricType aMetaParametricType) {
		// Do nothing...
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitModifier(lucava.metamodel.java.Modifier)
	 */
	@Override
	public void visitModifier(Modifier aModifier) {

		// Ajout eventuel du statut 'abstract'
		if (aModifier.isAbstract()) {
			this.mCurrentFileBuffer.print("abstract ");
		}

		// Ajout eventuel du statut 'static'
		if (aModifier.isStatic()) {
			this.mCurrentFileBuffer.print("static ");
		}

		// Ajout eventuel du statut 'final'
		if (aModifier.isFinal()) {
			this.mCurrentFileBuffer.print("final ");
		}
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitAnnotations(lucava.metamodel.java.Annotations)
	 */
	@Override
	public void visitAnnotations(Annotations annotations) {

		// Génération de toutes les annotations
		Iterator<String> annotationIt = annotations.getAnnotations().iterator();
		while (annotationIt.hasNext()) {
			this.mCurrentFileBuffer.printLnWithTab("@" + annotationIt.next());
		}
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitParameter(lucava.metamodel.java.Parameter)
	 */
	@Override
	public void visitParameter(Parameter aParameter) {

		// Ajout des éventuels modifiers du parametre
		aParameter.getModifier().accept(this);

		// Ajout du type du parametre
		aParameter.getType().accept(this);

		// Ajout du nom du parametre
		this.mCurrentFileBuffer.print(aParameter.getName());
	}

	@Override
	public void visitComments(Comments aComments) {
		// Génération de toutes les lignes de commentaires
		Iterator<String> commentIt = aComments.getAllCommentLines().iterator();
		while (commentIt.hasNext()) {
			this.mCurrentFileBuffer.printLnWithTab("* " + commentIt.next());
		}
	}

	/**
	 * Ajoute les éventuels commentaires de l'attribut.
	 */
	protected void generateComment(Attribute anAttribute) {

		// Si un commentaire est présent
		if (anAttribute.hasComment()) {

			// Début du commentaire
			this.mCurrentFileBuffer.printLnWithTab("/**");

			// Ajout de toutes les lignes de commentaire.
			anAttribute.getComments().accept(this);

			// fin du commentaire
			this.mCurrentFileBuffer.printLnWithTab("*/");
		}
	}

	/**
	 * Ajoute les éventuels commentaires de la classe.
	 */
	protected void generateComment(MetaClasse aClasse) {

		// Si un commentaire est présent
		if (aClasse.hasComment()) {

			// Début du commentaire
			this.mCurrentFileBuffer.printLnWithTab("/**");

			// Ajout de toutes les lignes de commentaire.
			aClasse.getComments().accept(this);

			// fin du commentaire
			this.mCurrentFileBuffer.printLnWithTab("*/");
		}
	}

	/**
	 * Ajoute les éventuels commentaires du paramètre.
	 */
	protected void generateComment(Parameter aParameter) {

		// Début du commentaire
		this.mCurrentFileBuffer.printWithTab("* @param ");

		this.mCurrentFileBuffer.printLn(aParameter.getName());

		// Ajout de toutes les lignes de commentaire du paramètre.
		aParameter.getComments().accept(this);
	}

	/**
	 * Ajoute les éventuels commentaires de la signature.
	 */
	protected void generateComment(MethodeSignature aSignature) {

		// Si un commentaire est présent
		if (aSignature.hasComment()) {

			// Début du commentaire
			this.mCurrentFileBuffer.printLnWithTab("/**");

			// Ajout de toutes les lignes de commentaire de la méthode.
			aSignature.getComments().accept(this);

			// Ajout des commentaires des paramètres.
			Iterator<Parameter> paramIt = aSignature.getParams().iterator();
			while (paramIt.hasNext()) {
				this.generateComment(paramIt.next());
			}

			// fin du commentaire
			this.mCurrentFileBuffer.printLnWithTab("*/");
		}
	}

	public void generateAttributeType(Attribute anAttribute) {

		// Si l'attribut est multiplie
		if (anAttribute.isMultiple()) {
			// Stockage dans une liste
			this.mCurrentFileBuffer.print("java.util.List<");

			// Ajout du type de l'attribut
			((MetaType) anAttribute.getType()).accept(this);

			this.mCurrentFileBuffer.print("> ");
		}
		// Sinon
		else {
			// Ajout direct du type de l'attribut
			((MetaType) anAttribute.getType()).accept(this);
		}
	}

	/**
	 * Suivre le lien.
	 * 
	 * @see lucava.metamodel.java.visitor.IJavaMetaModelVisitor#visitAttribute(lucava.metamodel.java.Attribute)
	 */
	@Override
	public void visitAttribute(Attribute anAttribute) {

		// Ajout des éventuels commentaires de l'attribut
		this.generateComment(anAttribute);

		this.mCurrentFileBuffer.printWithTab("");

		// Ajout des éventuels modifiers de l'attribut
		anAttribute.getModifier().accept(this);

		// Ajout de la visiblité de l'attribut
		anAttribute.getVisibility().accept(this);

		this.generateAttributeType(anAttribute);

		// Ajout du nom de l'attribut
		this.mCurrentFileBuffer.print(" " + anAttribute.getName());

		// Si l'attribut est multiplie
		if (anAttribute.isMultiple()) {

			// Création de la liste
			this.mCurrentFileBuffer.print(" = new java.util.ArrayList<");

			// Ajout du type de l'attribut
			((MetaType) anAttribute.getType()).accept(this);

			this.mCurrentFileBuffer.print(">()");
		}
		// Sinon, si l'attribut possède une valeur par défaut
		else if (anAttribute.getDefaultValue() != null) {
			// Ajout de la valeur par défaut
			this.mCurrentFileBuffer
					.print(" = " + anAttribute.getDefaultValue());
		}

		this.mCurrentFileBuffer.printLn(";");
		this.mCurrentFileBuffer.printNewLine();
	}

	/**
	 * Génère le Getter d'un attribut.
	 * 
	 * @param anAttribute
	 *            , Attribut dont le Getter est à générer.
	 */
	public void generateGetterForAttribute(Attribute anAttribute) {

		// Ajout du commentaire du Getter
		this.mCurrentFileBuffer.printLnWithTab("/**");
		this.mCurrentFileBuffer.printWithTab("* Getter de l'attribut "
				+ anAttribute.getName() + ".");

		// Si l'attribut possède un commentaire
		if (anAttribute.hasComment()) {
			// Ajout de commentaire de l'atribut
			this.mCurrentFileBuffer.printLnWithTab("* @return "
					+ anAttribute.getName() + " <br/>");
			anAttribute.getComments().accept(this);
		} else {
			this.mCurrentFileBuffer.printNewLine();
		}

		this.mCurrentFileBuffer.printLnWithTab("*/");

		this.mCurrentFileBuffer.printWithTab("public ");

		// Si l'attribut est 'static'
		if (anAttribute.getModifier().isStatic()) {
			// Le Getter l'est aussi
			this.mCurrentFileBuffer.print("static ");
		}

		// Ecriture du type de retour du getter
		this.generateAttributeType(anAttribute);

		// Ecriture du nom du getter
		this.mCurrentFileBuffer.printLn(" get"
				+ lucava.util.StringUtils.firstToUpper(anAttribute.getName())
				+ "(){");

		this.mCurrentFileBuffer.increaseTab();

		this.mCurrentFileBuffer.printWithTab("return ");

		// Si l'attribut n'est pas 'static'
		if (!anAttribute.getModifier().isStatic()) {
			// Ajout du 'this'
			this.mCurrentFileBuffer.print("this.");
		}

		this.mCurrentFileBuffer.print(anAttribute.getName() + ";");

		this.mCurrentFileBuffer.decreaseTab();
		this.mCurrentFileBuffer.printLnWithTab("}");
	}

	/**
	 * Génère le Setter d'un attribut.
	 * 
	 * @param anAttribute
	 *            , Attribut dont le Setter est à générer.
	 */
	public void generateSetterForAttribute(Attribute anAttribute) {

		// Parametre du Setter
		String setterParam = lucava.util.StringUtils.addArticleTo(anAttribute
				.getType().getName());

		// Ajout du commentaire du Setter.
		this.mCurrentFileBuffer.printLnWithTab("/**");
		this.mCurrentFileBuffer.printLnWithTab("* Setter de l'attribut "
				+ anAttribute.getName() + ".");

		// Ajout de commentaire de l'atribut
		this.mCurrentFileBuffer.printLnWithTab("*");
		this.mCurrentFileBuffer.printLnWithTab("* @param " + setterParam);
		// Si l'attribut possède un commentaire
		if (anAttribute.hasComment()) {
			anAttribute.getComments().accept(this);
		}
		this.mCurrentFileBuffer.printLnWithTab("*/");

		this.mCurrentFileBuffer.printWithTab("public ");

		// Si l'attribut est 'static'
		if (anAttribute.getModifier().isStatic()) {
			// Le Setter l'est aussi
			this.mCurrentFileBuffer.print("static ");
		}

		this.mCurrentFileBuffer.print("void ");

		// Ecriture du nom du setter
		this.mCurrentFileBuffer.print("set"
				+ lucava.util.StringUtils.firstToUpper(anAttribute.getName())
				+ "(");

		// Ajout du ype de l'attribut
		this.generateAttributeType(anAttribute);

		// Ajout du nom de la variable du setter
		this.mCurrentFileBuffer.printLn(" "
				+ lucava.util.StringUtils.addArticleTo(anAttribute.getType()
						.getName()) + ") {");

		// Ajout du corps du stter
		this.mCurrentFileBuffer.increaseTab();

		this.mCurrentFileBuffer.printWithTab("");

		// Si l'attribut n'est pas 'static'
		if (!anAttribute.getModifier().isStatic()) {
			this.mCurrentFileBuffer.print("this.");
		}

		this.mCurrentFileBuffer.print(anAttribute.getName() + " = ");
		this.mCurrentFileBuffer.printLn(setterParam + ";");

		this.mCurrentFileBuffer.decreaseTab();
		this.mCurrentFileBuffer.printLnWithTab("}");
	}

	/**
	 * Assigne si le générateur doit générer le visiteur.
	 */
	public void shouldGenerateVisitor(boolean aBoolean) {
		this.mShouldGenerateVisitor = aBoolean;
	}

	/**
	 * Indique si le générateur doit générer le visiteur.
	 */
	public boolean shouldGenerateVisitor() {
		return this.mShouldGenerateVisitor;
	}

	/**
	 * Retourne la liste des FileBuffer générés.
	 */
	public List<FileBuffer> getGeneratedFiles() {
		return mGeneratedFiles;
	}
}
